﻿using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Packaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;

namespace DataService.Office.Word
{
    public class XmlPackagePart
    {
        private static string wordmlNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
        private static string realtionmlNamespace = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
        private static XNamespace w = wordmlNamespace;
        public static XmlDocument GetXmlDocument(OpenXmlPart part)
        {
            XmlDocument xmlDoc = new XmlDocument();
            using (Stream partStream = part.GetStream())
            using (XmlReader partXmlReader = XmlReader.Create(partStream))
                xmlDoc.Load(partXmlReader);
            return xmlDoc;
        }
        public static XmlDocument GetXmlDocumentByMainDocumentPart(MainDocumentPart mainDocumentPart)
        {
            XmlDocument xmlDoc = new XmlDocument();
            using (Stream partStream = mainDocumentPart.GetStream())
            using (XmlReader partXmlReader = XmlReader.Create(partStream))
                xmlDoc.Load(partXmlReader);
            return xmlDoc;
        }
        public static System.Xml.Linq.XDocument WDExtractStyles(MainDocumentPart mainDocumentPart, bool getStylesWithEffectsPart = true)
        {
            System.Xml.Linq.XDocument styles = null;
            StylesPart stylesPart = null;
            if (getStylesWithEffectsPart)
                stylesPart = mainDocumentPart.StylesWithEffectsPart;
            else
                stylesPart = mainDocumentPart.StyleDefinitionsPart;

            if (stylesPart != null)
            {
                using (var reader = System.Xml.XmlNodeReader.Create(stylesPart.GetStream(FileMode.Open, FileAccess.Read)))
                {
                    // Create the XDocument:
                    styles = System.Xml.Linq.XDocument.Load(reader);
                }
            }
            return styles;
        }
        public static List<XElement> GetParagraphStyle(System.Xml.Linq.XDocument xDoc)
        {
            List<XElement> XElementList = (from style in xDoc.Root.Elements(w + "style")
                                           let tempTypeAttribute = style.Attribute(w + "type")
                                           where (tempTypeAttribute == null ? "" : tempTypeAttribute.Value) == "paragraph"
                                           select style).ToList();
            return XElementList;
        }
        public static string GetValueByXElementNodeName(XElement xElement, string nodeName, string attributeName)
        {
            IEnumerable<XElement> nodeFirst = xElement.Elements();
            foreach (var item in nodeFirst)
            {
                if (item.Name != w + nodeName)
                {
                    continue;
                }
                var iAttres = item.Attributes();
                if (iAttres == null)
                {
                    continue;
                }
                XAttribute xAttribute = iAttres.FirstOrDefault(x => x.Name == w + attributeName);
                if (xAttribute == null)
                {
                    continue;
                }
                return xAttribute.Value;
            }


            return xElement.Element(w + nodeName).Value;
        }
        public static List<XElement> GetParagraphXMLStyleByStyleId(System.Xml.Linq.XDocument xDoc, string styleId)
        {
            //style的层级覆盖优先关系：
            //run：是字符一级，paragraph：是段落
            //对run和paragraph，最优先的是default
            List<XElement> XElementList = (from style in xDoc.Root.Elements(w + "style")
                                           let tempTypeAttribute = style.Attribute(w + "type")
                                           let tempStyleId = style.Attribute(w + "styleId") == null ? "" : style.Attribute(w + "styleId").Value
                                           where (tempTypeAttribute == null ? "" : tempTypeAttribute.Value) == "paragraph" && tempStyleId == styleId
                                           select style).ToList();
            return XElementList;
        }


        public static XmlDocument GetXmlDocumentByPartName(Package targetPackage, string partName)
        {

            //此处可以优化,Uri可以传过来,通过Uri找部件
            XmlDocument xmlDoc = new XmlDocument();
            var list = targetPackage.GetParts();
            foreach (var item in list)
            {
                string uri = item.Uri.ToString();
                string[] tempArray = uri.Split('/');
                string tempPartName = tempArray[tempArray.Length - 1];
                if (tempPartName == partName)
                {
                    using (XmlReader partXmlReader = XmlReader.Create(item.GetStream()))
                    {
                        xmlDoc.Load(partXmlReader);
                    }
                }
            }
            return xmlDoc;
        }
        /// <summary>
        /// 读取document.xml.rels部件的XML值
        /// </summary>
        public static List<Dictionary<string, string>> ReturnListRelationshipValue(XmlDocument xmlDocument, List<string> relationIdList)
        {
            List<Dictionary<string, string>> dicList = new List<Dictionary<string, string>>();
            XmlNode xmlNode = xmlDocument.ChildNodes[1];
            XmlNodeList xmlNodeList = xmlNode.ChildNodes;
            foreach (XmlNode item in xmlNodeList)
            {
                string val = item.Attributes["Id"].Value;
                string existsVal = relationIdList.Find(x => x == val);
                if (!string.IsNullOrEmpty(existsVal))
                {
                    var tempArray = item.Attributes;
                    if (tempArray != null && tempArray.Count > 0)
                    {
                        Dictionary<string, string> dic = new Dictionary<string, string>();
                        foreach (XmlAttribute temp in tempArray)
                        {
                            dic[temp.Name] = temp.Value;
                        }
                        dicList.Add(dic);
                    }

                }
            }
            return dicList;
        }

        /// <summary>
        /// 读取document.xml.rels部件的XML值
        /// </summary>
        public static Dictionary<string, string> ReturnDicRelationshipValue(XmlDocument xmlDocument, List<string> relationIdList)
        {
            relationIdList = relationIdList.Distinct().ToList();
            Dictionary<string, string> dic = new Dictionary<string, string>();
            XmlNode xmlNode = xmlDocument.ChildNodes[1];
            XmlNodeList xmlNodeList = xmlNode.ChildNodes;
            foreach (XmlNode item in xmlNodeList)
            {
                string val = item.Attributes["Id"].Value;
                string existsVal = relationIdList.Find(x => x == val);
                if (!string.IsNullOrEmpty(existsVal))
                {
                    dic[val] = item.OuterXml;
                }
            }
            return dic;
        }
        /// <summary>
        /// 返回特殊的部件和RelationShip的关联关系
        /// </summary>
        /// <param name="xmlDocument"></param>
        /// <returns></returns>
        public static IEnumerable<KeyValuePair<string, string>> ReturnSpecialRelationshipValue(XmlDocument xmlDocument)
        {
            XmlNode xmlNode = xmlDocument.ChildNodes[1];
            XmlNodeList xmlNodeList = xmlNode.ChildNodes;
            foreach (XmlNode item in xmlNodeList)
            {
                string val = item.Attributes["Target"].Value;
                string idValue = item.Attributes["Id"].Value;
                string targetVal = val.ToLower();
                if (targetVal.Equals("styles.xml") || targetVal.Equals("numbering.xml"))
                {
                    KeyValuePair<string, string> kvp = new KeyValuePair<string, string>(idValue, item.OuterXml);
                    yield return kvp;
                }
            }
        }
        /// <summary>
        /// 返回特殊的部件和RelationShip的关联关系
        /// </summary>
        /// <param name="realtionShipIdList"></param>
        /// <returns></returns>
        public static IEnumerable<KeyValuePair<string, string>> ReturnRelationshipValueByShipId(XmlDocument xmlDocument, List<string> realtionShipIdList)
        {
            XmlNode xmlNode = xmlDocument.ChildNodes[1];
            XmlNodeList xmlNodeList = xmlNode.ChildNodes;
            foreach (XmlNode item in xmlNodeList)
            {
                string val = item.Attributes["Target"].Value;
                string idValue = item.Attributes["Id"].Value;
                string targetVal = val.ToLower();
                if (targetVal.Contains("numbering.xml")) continue;
                string tempStr = realtionShipIdList.Find(x => x.Equals(idValue));
                if (!string.IsNullOrEmpty(tempStr))
                {
                    KeyValuePair<string, string> kvp = new KeyValuePair<string, string>(idValue, item.OuterXml);
                    yield return kvp;
                }
            }
        }
        #region 返回命名空间
        /// <summary>
        /// 返回段落中命名空间前缀和命名空间值
        /// 备注：通过找到特性Key和Value对比源文件的docment节点的命名空间
        /// </summary>
        /// <param name="openXmlElement"></param>
        /// <returns></returns>
        public static Dictionary<string, string> GetOpenXmlElementNameSpace(OpenXmlElement openXmlElement)
        {
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(openXmlElement.OuterXml);
            Dictionary<string, string> dic = new Dictionary<string, string>();
            ReturnNodeNameSpace(xmlDoc.ChildNodes, dic);
            return dic;
        }
        private static void ReturnNodeNameSpace(XmlNodeList xmlNodeList, Dictionary<string, string> dic)
        {
            foreach (XmlNode item in xmlNodeList)
            {
                if (item.HasChildNodes)
                {
                    ReturnNodeNameSpace(item.ChildNodes, dic);
                }
                if (!string.IsNullOrEmpty(item.Prefix) && !string.IsNullOrEmpty(item.NamespaceURI))
                {
                    dic[item.Prefix] = item.NamespaceURI;

                }
            }
        }
        #endregion
        #region 返回段落中所有特性值
        /// <summary>
        /// 返回一个段落中所有特性值
        /// 备注：通过找到特性Key和Value对比源文件的docment节点的命名空间
        /// </summary>
        /// <param name="openXmlElement"></param>
        /// <returns></returns>
        public static List<string> GetXmlNodeXmlAttributeValue(OpenXmlElement openXmlElement)
        {
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(openXmlElement.OuterXml);
            List<string> list = new List<string>();
            ReturnNodeValue(xmlDoc.ChildNodes, list);
            return list;
        }
        private static void ReturnNodeValue(XmlNodeList xmlNodeList, List<string> list)
        {
            foreach (XmlNode item in xmlNodeList)
            {
                if (item.HasChildNodes)
                {
                    ReturnNodeValue(item.ChildNodes, list);
                }
                var attributesCollection = item.Attributes;
                if (attributesCollection != null)
                {
                    foreach (XmlAttribute temp in attributesCollection)
                    {
                        list.Add(temp.Value);
                    }
                }
            }
        }
        #endregion

        #region 返回段落中所有特性和值
        /// <summary>
        /// 返回一个段落中所有属性和值
        /// 备注：通过找到特性Key和Value对比源文件的docment节点的命名空间
        /// 以后若优化这里可以只返回节点值
        /// </summary>
        /// <param name="openXmlElement"></param>
        /// <returns></returns>
        public static Dictionary<string, string> GetXmlNodeXmlAttributeKeyAndValue(OpenXmlElement openXmlElement)
        {
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(openXmlElement.OuterXml);
            Dictionary<string, string> dic = new Dictionary<string, string>();
            ReturnNodeKeyValue(xmlDoc.ChildNodes, dic);
            return dic;
        }
        private static void ReturnNodeKeyValue(XmlNodeList xmlNodeList, Dictionary<string, string> dic)
        {
            foreach (XmlNode item in xmlNodeList)
            {
                if (item.HasChildNodes)
                {
                    ReturnNodeKeyValue(item.ChildNodes, dic);
                }
                //if (item.LocalName == "Choice")
                //{
                //    var aaaa = item.Attributes;
                //    foreach (XmlAttribute temp in aaaa)
                //    {
                //        Console.WriteLine(temp.OuterXml);
                //    }
                //}
                var attributesCollection = item.Attributes;
                if (attributesCollection != null)
                {
                    foreach (XmlAttribute temp in attributesCollection)
                    {
                        //节点名可能重复，这里加上唯一标识
                        dic[temp.Name + "_" + Guid.NewGuid()] = temp.Value;
                    }
                }
            }
        }
        #endregion


        /// <summary>
        /// 返回RelationID集合
        /// </summary>
        /// <param name="openXmlElement"></param>
        /// <returns></returns>
        public static List<string> GetRelationIdByOpenXmlElement(OpenXmlElement openXmlElement)
        {
            List<string> list = new List<string>();
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(openXmlElement.OuterXml);
            ReturnNode(xmlDoc.ChildNodes, list);
            return list;
        }
        private static void ReturnNode(XmlNodeList xmlNodeList, List<string> list)
        {
            foreach (XmlNode item in xmlNodeList)
            {
                if (item.HasChildNodes)
                {
                    ReturnNode(item.ChildNodes, list);
                }
                var attributesCollection = item.Attributes;
                if (attributesCollection != null)
                {
                    foreach (XmlAttribute temp in attributesCollection)
                    {
                        if (temp.NamespaceURI == realtionmlNamespace)
                        {
                            list.Add(temp.Value);
                        }
                    }
                }
                //Console.WriteLine(item.Value + "**" + item.Name+"**"+item.NamespaceURI);
            }
        }
        public static void AddRelsPartXML(Package package, string partName, List<Dictionary<string, string>> dicList)
        {
            XmlDocument xmlDoc = new XmlDocument();
            var list = package.GetParts();
            Uri docmentUri = new Uri(@"/word/document.xml", UriKind.Relative);
            Uri relsUri = new Uri(@"/word/_rels/document.xml.rels", UriKind.Relative);
            foreach (var item in list)
            {
                string uri = item.Uri.ToString();
                string[] tempArray = uri.Split('/');
                string tempPartName = tempArray[tempArray.Length - 1];
                if (tempPartName == partName)
                {
                    // foreach (var xmlNodeItem in dic)
                    //{
                    //这里有优化空间，可以参考PackageHelper.CreateInternalRelationship
                    // item.CreateRelationship(relsUri, System.IO.Packaging.TargetMode.Internal, realtionmlNamespace);
                    // }
                    using (XmlReader partXmlReader = XmlReader.Create(item.GetStream()))
                    {
                        xmlDoc.Load(partXmlReader);
                    }
                    if (xmlDoc.ChildNodes[1] != null)
                    {
                        #region 先校验节点对应的特性是否存在
                        //1.先取出原来的/word/_rels/document.xml.rels节点的值
                        List<string> jiaoyanList = new List<string>();
                        foreach (XmlNode xmlNode in xmlDoc.ChildNodes[1].ChildNodes)
                        {
                            foreach (XmlAttribute xmlAttribute in xmlNode.Attributes)
                            {
                                jiaoyanList.Add(xmlAttribute.Name + xmlAttribute.Value);
                            }
                        }
                        #endregion

                        foreach (var temp in dicList)
                        {
                            //若子节点的命名空间与父节点的命名空间一致时，则子节点隐藏xmlns属性,若不不一致时，则子节点会有xmlns=""
                            XmlElement newNodel = xmlDoc.CreateElement(null, "Relationship", xmlDoc.ChildNodes[1].NamespaceURI);
                            foreach (var tempXmlArrtr in temp)
                            {
                                //2.若不存在jiaoyanList集合中，则添加特性及节点
                                string jiaoyanResult = jiaoyanList.Find(x => x == (tempXmlArrtr.Key + tempXmlArrtr.Value));
                                if (!string.IsNullOrEmpty(jiaoyanResult))
                                {
                                    break;
                                }
                                newNodel.SetAttribute(tempXmlArrtr.Key, tempXmlArrtr.Value);
                            }
                            xmlDoc.ChildNodes[1].AppendChild(newNodel);
                        }
                    }
                    using (StreamWriter streamWriter = new StreamWriter(item.GetStream(FileMode.Create)))
                    {
                        xmlDoc.Save(streamWriter);
                    }
                }
            }
        }
        public static void AddRelsPartXML(Package tagretpackage, Dictionary<string, string> dic)
        {
            var list = tagretpackage.GetParts();
            Uri docmentUri = new Uri(@"/word/document.xml", UriKind.Relative);
            Uri relsUri = new Uri(@"/word/_rels/document.xml.rels", UriKind.Relative);
            if (tagretpackage.PartExists(relsUri))
            {
                XmlDocument xmlDoc = new XmlDocument();
                PackagePart packagePart = tagretpackage.GetPart(relsUri);
                Stream stream = packagePart.GetStream();
                using (XmlReader partXmlReader = XmlReader.Create(stream))
                {
                    xmlDoc.Load(partXmlReader);
                }
                List<string> nodeOutXmlList = new List<string>();
                XmlNodeList xmlNodeList = xmlDoc.ChildNodes[1].ChildNodes;
                XmlNode relationShipsNode = xmlDoc.ChildNodes[1];
                foreach (XmlNode temp in xmlNodeList)
                {
                    if (temp.Attributes["Id"] != null)
                    {
                        string val = temp.Attributes["Id"].Value;
                        if (dic.ContainsKey(val))
                        {
                            continue;
                        }
                    }
                    nodeOutXmlList.Add(temp.OuterXml);
                    relationShipsNode.RemoveChild(temp);
                }
                if (dic.Count > 0)
                {
                    var newNodeList = dic.Select(x => x.Value).ToList();
                    nodeOutXmlList.AddRange(newNodeList);
                }
                nodeOutXmlList = nodeOutXmlList.Distinct().ToList();
                string xmlString = WirteRelsXML(nodeOutXmlList);
                xmlDoc.LoadXml(xmlString);
                using (StreamWriter streamWriter = new StreamWriter(packagePart.GetStream(FileMode.Create)))
                {
                    xmlDoc.Save(streamWriter);
                }
            }
            else
            {
                XmlDocument xmlDoc = new XmlDocument();
                var nodeOutXmlList = dic.Values.Select(x => x).ToList();
                string xmlString = WirteRelsXML(nodeOutXmlList);
                xmlDoc.LoadXml(xmlString);
                string relsContentType = "application/vnd.openxmlformats-package.relationships+xml";
                System.IO.Packaging.PackagePart newPart = tagretpackage.CreatePart(relsUri, relsContentType, System.IO.Packaging.CompressionOption.Normal);
                using (StreamWriter streamWriter = new StreamWriter(newPart.GetStream(FileMode.Create)))
                {
                    xmlDoc.Save(streamWriter);
                }
            }
        }

        public static void AddRelsPartXML(Package tagretpackage, string partName, Dictionary<string, string> dic)
        {
            XmlDocument xmlDoc = new XmlDocument();
            var list = tagretpackage.GetParts();
            Uri docmentUri = new Uri(@"/word/document.xml", UriKind.Relative);
            Uri relsUri = new Uri(@"/word/_rels/document.xml.rels", UriKind.Relative);
            foreach (var item in list)
            {
                string uri = item.Uri.ToString();
                string[] tempArray = uri.Split('/');
                string tempPartName = tempArray[tempArray.Length - 1];
                if (tempPartName == partName)
                {
                    // foreach (var xmlNodeItem in dic)
                    //{
                    //这里有优化空间，可以参考PackageHelper.CreateInternalRelationship
                    // item.CreateRelationship(relsUri, System.IO.Packaging.TargetMode.Internal, realtionmlNamespace);
                    // }
                    using (XmlReader partXmlReader = XmlReader.Create(item.GetStream()))
                    {
                        xmlDoc.Load(partXmlReader);
                    }
                    List<string> nodeOutXmlList = new List<string>();
                    XmlNodeList xmlNodeList = xmlDoc.ChildNodes[1].ChildNodes;
                    XmlNode relationShipsNode = xmlDoc.ChildNodes[1];
                    foreach (XmlNode temp in xmlNodeList)
                    {
                        if (temp.Attributes["Id"] != null)
                        {
                            string val = temp.Attributes["Id"].Value;
                            if (dic.ContainsKey(val))
                            {
                                continue;
                            }
                        }
                        nodeOutXmlList.Add(temp.OuterXml);
                        relationShipsNode.RemoveChild(temp);
                    }
                    if (dic.Count > 0)
                    {
                        var newNodeList = dic.Select(x => x.Value).ToList();
                        nodeOutXmlList.AddRange(newNodeList);
                    }
                    nodeOutXmlList = nodeOutXmlList.Distinct().ToList();
                    string xmlString = WirteRelsXML(nodeOutXmlList);
                    xmlDoc.LoadXml(xmlString);
                    using (StreamWriter streamWriter = new StreamWriter(item.GetStream(FileMode.Create)))
                    {
                        xmlDoc.Save(streamWriter);
                    }
                }
            }
        }

        public static string WirteRelsXML(List<string> list)
        {
            //string a = "1.0";
            // string root = "<?xml version=" + a + " encoding=\"UTF-8\" standalone=\"yes\"?>";
            StringBuilder stringBuilder = new StringBuilder();
            // stringBuilder.AppendLine(root);
            stringBuilder.AppendLine("<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">");
            foreach (var item in list)
            {
                stringBuilder.AppendLine(item);
            }
            stringBuilder.AppendLine("</Relationships>");
            return stringBuilder.ToString();
        }
        public static void AddNumberingXML(Package tagertPackage, Dictionary<string, string> nameSpaceList, Dictionary<string, string> dic)
        {
            if (nameSpaceList == null || nameSpaceList.Count == 0) return;
            Uri numberingUri = new Uri(@"/word/numbering.xml", UriKind.Relative);
            if (tagertPackage.PartExists(numberingUri)) return;
            XmlDocument xmlDoc = new XmlDocument();
            string xmlString = WriteNumberingXML(nameSpaceList, dic);
            xmlDoc.LoadXml(xmlString);
            string relsContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml";
            System.IO.Packaging.PackagePart newPart = tagertPackage.CreatePart(numberingUri, relsContentType, System.IO.Packaging.CompressionOption.Normal);
            using (StreamWriter streamWriter = new StreamWriter(newPart.GetStream(FileMode.Create)))
            {
                xmlDoc.Save(streamWriter);
            }
        }
        /// <summary>
        /// 返回numbering.xml的元素
        /// </summary>
        /// <param name="nameSpaceList">Key:命名前缀 Value:命名空间,例如 w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"</param>
        /// <param name="nodeList"></param>
        /// <returns></returns>
        public static string WriteNumberingXML(Dictionary<string, string> nameSpaceList, Dictionary<string, string> nodeDic)
        {
            if (nameSpaceList == null || nameSpaceList.Count == 0) return string.Empty;
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append("<w:numbering");
            #region 处理命名空间
            foreach (var item in nameSpaceList)
            {
                stringBuilder.Append(" xmlns:").Append(item.Key).Append("=\"").Append(item.Value).Append("\"");
            }
            stringBuilder.Append(">");
            #endregion
            #region 节点元素
            foreach (var item in nodeDic)
            {
                stringBuilder.AppendLine(item.Value);
            }
            stringBuilder.AppendLine("</w:numbering>");
            #endregion
            return stringBuilder.ToString();
        }
        /// <summary>
        /// 返回Document.xml中Document节点的所有命名空间
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<KeyValuePair<string, string>> ReturnDocumentxmlns(MainDocumentPart mainPart)
        {
            //第一种方法
            return mainPart.Document.NamespaceDeclarations;
            //第二中,优化速度的时候看那种速度块
            //XmlDocument xmlDocument = WordXML.GetXmlDocumentByMainDocumentPart(mainPart);

            //XmlNode xmlNode = xmlDocument.ChildNodes[1];
            //foreach (XmlAttribute item in xmlNode.Attributes)
            //{
            //    Console.WriteLine(item.Name + "啊啊啊" + item.Value);
            //}
        }

    }




}
